﻿
using MapsterMapper;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SSPivot.AspNetCore.Abstractions.User;
using SSPivot.AspNetCore.Repository;
using SSPivot.Common;
using SSPivot.Common.Cache;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;

namespace SSPivot.AspNetCore.Services
{
    public abstract class ApplicationService: IApplicationService
    {
        private ILoggerFactory _loggerFactory;

        private readonly IDbContextFactory _dbContextFactory;
        protected IDictionary<Type, object> CachedServices = new Dictionary<Type, object>();
        private ICurrentUser _user;
        /// <summary>
        /// AutoMapper
        /// </summary>
        public IMapper Mapper { get; set; }
        public ICacheService Cache { get; set; }

        /// <summary>
        /// 日志工厂
        /// </summary>
        public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
        /// <summary>
        /// 用户信息
        /// </summary>
        public ICurrentUser User => LazyGetRequiredService(ref _user);

        /// <summary>
        /// 日志
        /// </summary>
        protected ILogger Logger => _lazyLogger.Value;
        protected readonly object ServiceProviderLock = new object();
        private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) );
        /// <summary>
        /// 下上文服务
        /// </summary>
        public AppContextProvider AppContextProvider { get; set; }

        public IServiceProvider ServiceProvider { get; set; }
      
        protected TService LazyGetRequiredService<TService>(ref TService reference)
        {
            if (reference == null)
            {
                lock (ServiceProviderLock)
                {
                    if (reference == null)
                    {
                        reference = ServiceProvider.GetRequiredService<TService>();
                    }
                }
            }

            return reference;
        }

        /// <summary>
        /// 获得懒加载服务
        /// </summary>
        /// <typeparam name="TService">服务接口</typeparam>
        /// <returns></returns>
        [NonAction]
        public virtual TService LazyGetRequiredService<TService>()
        {
            return (TService)LazyGetRequiredService(typeof(TService));
        }

        /// <summary>
        /// 根据服务类型获得懒加载服务
        /// </summary>
        /// <param name="serviceType">服务类型</param>
        /// <returns></returns>
        [NonAction]
        public virtual object LazyGetRequiredService(Type serviceType)
        {
            return CachedServices.GetOrAdd(serviceType, () => ServiceProvider.GetRequiredService(serviceType));
        }


        /// <summary>
        /// 启动事务
        /// </summary>
        /// <param name="action"></param>
        /// <param name="scopeOption"></param>
        /// <param name="isolationLevel"></param>
        public void StartTransaction(Action action, TransactionScopeOption scopeOption = TransactionScopeOption.Required, IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            using (TransactionScope trans = new TransactionScope(scopeOption, new TransactionOptions() { IsolationLevel = isolationLevel }))
            {
                action();
                trans.Complete();
            }
        }

        /// <summary>
        /// 启动事务
        /// </summary>
        /// <param name="func"></param>
        /// <param name="scopeOption"></param>
        /// <param name="isolationLevel"></param>
        public T StartTransaction<T>(Func<T> func, TransactionScopeOption scopeOption = TransactionScopeOption.Required, IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
        {
            using (TransactionScope trans = new TransactionScope(scopeOption, new TransactionOptions() { IsolationLevel = isolationLevel }))
            {
                T t = func();
                trans.Complete();
                return t;
            }
        } 
    }
}
